package com.github.ghsea.scheduler.timer;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author netty
 */
public class HashedWheelTimeout implements Timeout {

    static final Logger logger = LoggerFactory.getLogger(HashedWheelTimeout.class);

    private static final int ST_INIT = 0;
    private static final int ST_CANCELLED = 1;
    private static final int ST_EXPIRED = 2;

    private final TimerTask task;
    final long deadline;
    volatile int stopIndex;
    volatile long remainingRounds;

    private final AtomicInteger state = new AtomicInteger(ST_INIT);

    private HashedWheelTimer timer;

    HashedWheelTimeout(TimerTask task, long deadline, HashedWheelTimer timer) {
        this.task = task;
        this.deadline = deadline;
        this.timer = timer;
    }

    @Override
    public Timer getTimer() {
        return timer;
    }

    @Override
    public TimerTask getTask() {
        return task;
    }

    @Override
    public void cancel() {
        if (!state.compareAndSet(ST_INIT, ST_CANCELLED)) {
            // TODO return false
            return;
        }

        timer.wheel[stopIndex].remove(this);
    }

    @Override
    public boolean isCancelled() {
        return state.get() == ST_CANCELLED;
    }

    @Override
    public boolean isExpired() {
        return state.get() != ST_INIT;
    }

    public void expire() {
        //being guhai.
//        if (!state.compareAndSet(ST_INIT, ST_EXPIRED)) {
//            return;
//        }
        //End

        try {
            task.run(this);
        } catch (Throwable t) {
            if (logger.isWarnEnabled()) {
                logger.warn(
                        "An exception was thrown by " +
                                TimerTask.class.getSimpleName() + '.', t);
            }
        }
    }

    @Override
    public String toString() {
        long currentTime = System.nanoTime();
        long remaining = deadline - currentTime;

        StringBuilder buf = new StringBuilder(192);
        buf.append(getClass().getSimpleName());
        buf.append('(');

        buf.append("deadline: ");
        if (remaining > 0) {
            buf.append(remaining);
            buf.append(" ms later, ");
        } else if (remaining < 0) {
            buf.append(-remaining);
            buf.append(" ms ago, ");
        } else {
            buf.append("now, ");
        }

        if (isCancelled()) {
            buf.append(", cancelled");
        }

        return buf.append(')').toString();
    }
}
